/*
Copyright 2025 PerfXLab (Beijing) Technologies Co., Ltd.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
#pragma once
#include "OpenSparseBLAS_func.h"
#include "OpenSparseBLAS_type.h"
#include <stdlib.h>

/**
 * @ingroup OpenSpB_matrix
 * @brief A structure to represent matirixs
 * @details This class stores basic information about a matrix, which is inherited by other concrete matrix classes of various formats.
 */
struct OpenSpB_matrix_opaque
{
    OpenSpB_type element_type;                   /**< Element data type */
    OpenSpB_matrix_format_value_ext format_type; /**<  Marker of matrix format */
    OpenSpB_index row;                           /**< Number of rows*/
    OpenSpB_index col;                           /**< Number of rows */
    size_t element_size;                         /**< byte number of a single element.*/
};

/**
 * @ingroup OpenSpB_matrix
 * @defgroup OpenSpB_matrix_CSC_or_CSR
 * @brief A CSC/CSR format matrix class
 * @details Sparse matrix stored in traditional CSC (Compressed sparse column) or CSR (compressed sparse row) format. Since these two formats have symmetry, a class is reused to manage both formats.
 * @{
 */

typedef struct
{
    struct OpenSpB_matrix_opaque matrix_base;
    void *val;              ///< Value of each non-zero element.
    OpenSpB_index *indices; ///< Row coordinate for CSC, and col coordinate for CSR.
    OpenSpB_index *ptr;     ///< Col initial position for CSC, and row initial position for CSR.
    OpenSpB_index ptr_len;  ///< Length of ptr array.
    OpenSpB_index nnz;      ///< Total non-zero elements.
    OpenSpB_index capacity; ///< the capacity of val, indices array.
} OpenSpB_matrix_CSC_or_CSR;

OpenSpB_index OpenSpB_matrix_CSC_or_CSR_nnz(OpenSpB_matrix_CSC_or_CSR *m);

OpenSpB_info OpenSpB_matrix_CSC_or_CSR_new(OpenSpB_matrix *m_in,
                                           OpenSpB_type type,
                                           OpenSpB_index row,
                                           OpenSpB_index col,
                                           OpenSpB_matrix_format_value_ext format_type);

/**
 * @anchor OpenSpB_matrix_CSC_or_CSR_build
 * @name OpenSpB_matrix_CSC_or_CSR_build
 * @brief Store elements from tuples into a CSC/CSR matrix.
 * @param[in,out] m_in An existing CSC/CSR Matrix object to store the result.
 * @param[in] row_indices Pointer to an array of row indices.
 * @param[in] col_indices Pointer to an array of column indices.
 * @param[in] values Pointer to an array of scalars of a type that is compatible with the domain of matrix, m_in.
 * @param[in] n The number of entries contained in each array (the same for row_indices, col_indices, and values).
 * @return OpenSpB_info
 */
///@{
#define GRB_MATRIX_CSC_OR_CSR_FAST_BUILD_EXT(type, T)            \
    OpenSpB_info OpenSpB_matrix_CSC_or_CSR_fast_build_##T##_ext( \
        OpenSpB_matrix m_in,                                     \
        const OpenSpB_index *row_indices,                        \
        const OpenSpB_index *col_indices,                        \
        const type *values,                                      \
        const OpenSpB_index n);
GRB_MATRIX_CSC_OR_CSR_FAST_BUILD_EXT(bool, BOOL);
GRB_MATRIX_CSC_OR_CSR_FAST_BUILD_EXT(int8_t, INT8);
GRB_MATRIX_CSC_OR_CSR_FAST_BUILD_EXT(int16_t, INT16);
GRB_MATRIX_CSC_OR_CSR_FAST_BUILD_EXT(int32_t, INT32);
GRB_MATRIX_CSC_OR_CSR_FAST_BUILD_EXT(int64_t, INT64);
GRB_MATRIX_CSC_OR_CSR_FAST_BUILD_EXT(uint8_t, UINT8);
GRB_MATRIX_CSC_OR_CSR_FAST_BUILD_EXT(uint16_t, UINT16);
GRB_MATRIX_CSC_OR_CSR_FAST_BUILD_EXT(uint32_t, UINT32);
GRB_MATRIX_CSC_OR_CSR_FAST_BUILD_EXT(uint64_t, UINT64);
GRB_MATRIX_CSC_OR_CSR_FAST_BUILD_EXT(float, FP32);
GRB_MATRIX_CSC_OR_CSR_FAST_BUILD_EXT(double, FP64);
#undef GRB_MATRIX_CSC_OR_CSR_FAST_BUILD_EXT

#define OpenSpB_MATRIX_CSC_OR_CSR_BUILD(type, T)      \
    OpenSpB_info OpenSpB_matrix_CSC_or_CSR_build_##T( \
        OpenSpB_matrix m_in,                          \
        const OpenSpB_index *row_indices,             \
        const OpenSpB_index *col_indices,             \
        const type *values,                           \
        const OpenSpB_index n);
OpenSpB_MATRIX_CSC_OR_CSR_BUILD(bool, BOOL);
OpenSpB_MATRIX_CSC_OR_CSR_BUILD(int8_t, INT8);
OpenSpB_MATRIX_CSC_OR_CSR_BUILD(int16_t, INT16);
OpenSpB_MATRIX_CSC_OR_CSR_BUILD(int32_t, INT32);
OpenSpB_MATRIX_CSC_OR_CSR_BUILD(int64_t, INT64);
OpenSpB_MATRIX_CSC_OR_CSR_BUILD(uint8_t, UINT8);
OpenSpB_MATRIX_CSC_OR_CSR_BUILD(uint16_t, UINT16);
OpenSpB_MATRIX_CSC_OR_CSR_BUILD(uint32_t, UINT32);
OpenSpB_MATRIX_CSC_OR_CSR_BUILD(uint64_t, UINT64);
OpenSpB_MATRIX_CSC_OR_CSR_BUILD(float, FP32);
OpenSpB_MATRIX_CSC_OR_CSR_BUILD(double, FP64);
#undef OpenSpB_MATRIX_CSC_OR_CSR_BUILD

///@}

/**
 * @anchor OpenSpB_matrix_CSC_or_CSR_setElement
 * @name OpenSpB_matrix_CSC_or_CSR_setElement
 * @brief Set a single element in a CSC/CSR matrix
 * @param[in,out] m_in An existing CSC/CSR matrix for which an element is to be assigned
 * @param[in] value Scalar to assign. Its domain (type) must be compatible with the domain of m_in.
 * @param[in] row_index Row index of element to be assigned.
 * @param[in] col_index Column index of element to be assigned.
 * @return OpenSpB_info
 */
///@{
#define OpenSpB_MATRIX_CSC_OR_CSR_SETELEMENT(type, T)                              \
    OpenSpB_info OpenSpB_matrix_CSC_or_CSR_setElement_##T(OpenSpB_matrix m_in,     \
                                                          type value,              \
                                                          OpenSpB_index row_index, \
                                                          OpenSpB_index col_index)
OpenSpB_MATRIX_CSC_OR_CSR_SETELEMENT(bool, BOOL);
OpenSpB_MATRIX_CSC_OR_CSR_SETELEMENT(int8_t, INT8);
OpenSpB_MATRIX_CSC_OR_CSR_SETELEMENT(int16_t, INT16);
OpenSpB_MATRIX_CSC_OR_CSR_SETELEMENT(int32_t, INT32);
OpenSpB_MATRIX_CSC_OR_CSR_SETELEMENT(int64_t, INT64);
OpenSpB_MATRIX_CSC_OR_CSR_SETELEMENT(uint8_t, UINT8);
OpenSpB_MATRIX_CSC_OR_CSR_SETELEMENT(uint16_t, UINT16);
OpenSpB_MATRIX_CSC_OR_CSR_SETELEMENT(uint32_t, UINT32);
OpenSpB_MATRIX_CSC_OR_CSR_SETELEMENT(uint64_t, UINT64);
OpenSpB_MATRIX_CSC_OR_CSR_SETELEMENT(float, FP32);
OpenSpB_MATRIX_CSC_OR_CSR_SETELEMENT(double, FP64);
#undef OpenSpB_MATRIX_CSC_OR_CSR_SETELEMENT
///@}

/**
 * @anchor OpenSpB_matrix_CSC_or_CSR_extractElement
 * @name OpenSpB_matrix_CSC_or_CSR_extractElement
 * @brief Extract a single element from a CSC/CSR matrix
 * @param[in,out] val An existing scalar whose domain is compatible with the domain of matrix **m_in**. On successful return, this scalar holds the result of the extract. Any previous value stored in **val** is overwritten.
 * @param[in] m_in The CSC/CSR matrix from which an element is extracted.
 * @param[in] row_index The row index of location in **m_in** to extract.
 * @param[in] col_index The row index of location in **m_in** to extract.
 * @return OpenSpB_info
 */
///@{
#define OpenSpB_MATRIX_CSC_OR_CSR_EXTRACTELEMENT(type, T)                                \
    OpenSpB_info OpenSpB_matrix_CSC_or_CSR_extractElement_##T(type *val,                 \
                                                              const OpenSpB_matrix m_in, \
                                                              OpenSpB_index row_index,   \
                                                              OpenSpB_index col_index)

OpenSpB_MATRIX_CSC_OR_CSR_EXTRACTELEMENT(bool, BOOL);
OpenSpB_MATRIX_CSC_OR_CSR_EXTRACTELEMENT(int8_t, INT8);
OpenSpB_MATRIX_CSC_OR_CSR_EXTRACTELEMENT(int16_t, INT16);
OpenSpB_MATRIX_CSC_OR_CSR_EXTRACTELEMENT(int32_t, INT32);
OpenSpB_MATRIX_CSC_OR_CSR_EXTRACTELEMENT(int64_t, INT64);
OpenSpB_MATRIX_CSC_OR_CSR_EXTRACTELEMENT(uint8_t, UINT8);
OpenSpB_MATRIX_CSC_OR_CSR_EXTRACTELEMENT(uint16_t, UINT16);
OpenSpB_MATRIX_CSC_OR_CSR_EXTRACTELEMENT(uint32_t, UINT32);
OpenSpB_MATRIX_CSC_OR_CSR_EXTRACTELEMENT(uint64_t, UINT64);
OpenSpB_MATRIX_CSC_OR_CSR_EXTRACTELEMENT(float, FP32);
OpenSpB_MATRIX_CSC_OR_CSR_EXTRACTELEMENT(double, FP64);
#undef OpenSpB_MATRIX_CSC_OR_CSR_EXTRACTELEMENT
///@}

OpenSpB_info OpenSpB_matrix_CSC_or_CSR_free(OpenSpB_matrix *m_in);

/**@}*/
